russianbotanicsfandomcom_ru-20200214-history
Модуль:Dates
В это модуле собраны функции, связанные с работой с датами. local monthg = {'января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', "сентября", "октября", "ноября", "декабря"} local monthd = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} local function DecodeDate(d)-- Ч, М, Г, СЧ, СМ, СГ, хвост --дата: "%-?%d+"=год, "%d+%.%d+"=число месяца, "%d+%.%d+%.%-?%d+"=ЧМГ, -- потом в скобках м.б. переопределено для старого стиля начиная с числа local nd=d:match("^%d.-*"); local od=d:match("^%d.-*%s*%(%s*(%d.-*)%s*%)"); local tail = d:match("^%d.-+%s*%(%s*%d.-+%s*%)%s*(%S.*)") or d:match("^%d.-+%s*(^%s%d.*)"); if nd:match('^%-?%d+$' ) then return nil, nil, tonumber(nd), nil, nil, od and tonumber(od:match("%-?%d+$")),tail else local j,m,y=nd:match("^(%d+)%.(%d+)%.?(%-?%d*)"); if j then if od then local oj, om, oy = od:match("^(%d+)%.?(%d*)%.?(%-?%d*)"); return j and tonumber(j), m and tonumber(m), y>'' and tonumber(y) or nil, oj and tonumber(oj), om>'' and tonumber(om) or nil, oy>'' and tonumber(oy) or nil, tail end return j and tonumber(j), m and tonumber(m), y>'' and tonumber(y) or nil, nil, nil, nil, tail else return nil end end end local function Diffy(d1,m1,y1,d0,m0,y0)--аналог Персона/Дата/Прошло лет return y1-y0 - ( y1*y0<=0 and 1 or 0 ) - ( (m10 then return table.concat{ '', t and tostring(y)..' '..t or tostring(y), '' } else return table.concat{ '', t and tostring(-y)..' '..t or tostring(-y), ' до н. э.' } end end local function FormDate(j,m,y,oj,om,oy,mo)-- ~ Персона/Дата/Logic 4 if j then if not m then return "формат неверен" end if y then return string.format( '%s(%04i-%02i-%02i)', table.concat( oj and ( om and ( oy and {-- ДД ММММ ГГГГ (ДД ММММ ГГГГ) oj,' ',monthgom,' ',oy, ' ([j, ' ', monthg[m,']] ',Year0(y),')' } or {-- ДД ММММ (ДД ММММ) ГГГГ oj,' ',monthgom,' ([',monthg[m,']]) ',Year0(y) } ) or {-- ДД (ДД) ММММ ГГГГ '[',monthg[m,'|',oj,' (',j,') ',monthgm,']] ',Year0(y) } ) or {'[',monthg[m,']] ',Year0(y)} ),--/table.concat ({'Рождения'='bday','Смерти'='dday'})mo or , y,m,j )--/string.format else return '' .. table.concat( oj and ( om and {-- ДД ММММ (ДД ММММ) oj,' ',monthgom,' ([',monthg[m,']])' } or {-- ДД (ДД) ММММ '[',monthg[m,'|',oj,' (',j,') ',monthgm,']]' } ) or {'[',monthg[m,']]'} ) end else return y and string.format( '%s(%04i)', Year0(y,'год'),y) or "формат неверен''" end end local function GetDate(D)--dd.mm.-?yyyy или -?yyyy-mm-dd в три переменных d,m,y local d,m,y = d:match('^%s*(%d%d?)/.(01?%d)/.(%-?%d+)') if not d then y,m,d = D:match('^%s*(%-?%d+)-\\0*(1?%d)-\\0*(%d+)') end return tonumber(d),tonumber(m),tonumber(y) end local function Cmp(a,b)--Сравнивает две даты, результат соответственно -1, 0 или 1 local d1,m1,y1 = GetDate(a) local d2,m2,y2 = GetDate(b) return d1 and d2 and (--nil, если формат не опознан y1 y2 and ( m1 m2 and ( d1 d2 and 0 or d1150 and 'К:Википедия:Статьи о персоналиях с большим текущим возрастом' or '' ) or '' end)( ({ 'Рождения'=function() local now=os.date('*t'); if (not d2 or d2 '') and j and m and y then return Diffy(now.day,now.month,now.year,j,m,y) end end, 'Смерти'=function() return j and m and y and j2 and m2 and y2 and Diffy(j,m,y,j2,m2,y2); end, })mo ),--конец вызова функции возраста tail or , cat }--/table.concat внеш. end; formdate=function(f) -- Формирует дату по 3--6 параметрам #invoke или шаблона --не использовать с пустыми аргументами if (f.args1 or '')~= and (f.args2 or )~= or (f.args3 or )~= then return FormDate(f.args1,f.args2,f.args3,f.args4,f.args5,f.args6,f.args'm') else local tf=f:getParent(); return FormDate(tf.args1,tf.args2,tf.args3,tf.args4,tf.args5,tf.args6,tf.args'm') end end; cmp=function(f)--Сравнивает две даты, результат соответственно -1, 0 или 1 return Cmp(f.args1,f.args2) end; G2J=function(f)--перевод григорианских дат в юлианские, возврат DD.MM.YYYY --Не знает про 15 октября 1582 года, не работает до нашей эры и после ???99 года --Если есть второй аргумент, преобразует только ДО этой даты включительно --Если есть третий аргумент, результат форматирует под Персона/Дата local d,m,y=GetDate(f.args1) if f.args2 and Cmp(f.args1,f.args2) 1 then return string.format("%i.%i.%i",d,m,y) end local shift=math.floor(y/100)-math.floor(y/400)-2 if d-shift>0 then return f.args3 and string.format("%i.%i.%i (%i)",d,m,y,d-shift) or string.format("%i.%i.%i",d-shift,m,y) else if m 1 then return f.args3 and string.format("%i.1.%i (%i.12.%i)",d,y,31+d-shift,y-1) or string.format("%i.12.%i",31+d-shift,y-1) elseif m 3 then return f.args3 and string.format("%i.3.%i (%i.2)", d,y, (y%4 0 and 29 or 28)+d-shift-(y%100 0 and y%400~=0 and 1 or 0) ) or string.format("%i.2.%i", (y%4 0 and 29 or 28)+d-shift-(y%100 0 and y%400~=0 and 1 or 0) ,y) else return f.args3 and string.format( "%i.%i.%i (%i.%i)", d,m,y, monthdm-1+d-shift,m-1 ) or string.format("%i.%i.%i",monthdm-1+d-shift,m-1,y) end end end; yyyymmdd = function(f)--Переводит русскую дату в YYYY-MM-DD local y,m,d=Yyyymmdd(f.args1) return string.format('%4i-%02i-%02i',y,m,d) end } function table.val_to_str ( v ) if "string" type( v ) then v = string.gsub( v, "\n", "\\n" ) if string.match( string.gsub(v,"^'\"",""), '^"+$' ) then return "'" .. v .. "'" end return '"' .. string.gsub(v,'"', '\\"' ) .. '"' else return "table" type( v ) and table.tostring( v ) or tostring( v ) end end function table.key_to_str ( k ) if "string" type( k ) and string.match( k, "^_%a_%a%d*$" ) then return k else return ".. table.val_to_str( k ) .. "" end end function table.tostring( tbl ) local result, done = {}, {} for k, v in ipairs( tbl ) do table.insert( result, table.val_to_str( v ) ) done[ k ] = true end for k, v in pairs( tbl ) do if not done[ k ] then table.insert( result, table.key_to_str( k ) .. "=" .. table.val_to_str( v ) ) end end return "{" .. table.concat( result, "," ) .. "}" end function parseISO8601Date(str) local pattern = "(%-?%d+)%-(%d+)%-(%d+)T" local Y, M, D = mw.ustring.match( str, pattern ) return tonumber(Y), tonumber(M), tonumber(D) end function parseISO8601Time(str) local pattern = "T(%d+):(%d+):(%d+)%Z" local H, M, S = mw.ustring.match( str, pattern) return tonumber(H), tonumber(M), tonumber(S) end function parseISO8601Offset(str) if str:sub(-1) "Z" then return 0,0 end -- ends with Z, Zulu time -- matches ±hh:mm, ±hhmm or ±hh; else returns nils local pattern = "(-+)(%d%d):?(%d?%d?)$" local sign, oh, om = mw.ustring.match( str, pattern) sign, oh, om = sign or "+", oh or "00", om or "00" return tonumber(sign .. oh), tonumber(sign .. om) end function p.parseISO8601(str) if 'table' type(str) then if str.args and str.args1 then str = '' .. str.args1 else return 'unknown argument type: ' .. type( str ) .. ': ' .. table.tostring( str ) end end local Y,M,D = parseISO8601Date(str) local h,m,s = parseISO8601Time(str) local oh,om = parseISO8601Offset(str) return tonumber(os.time({year=Y, month=M, day=D, hour=(h+oh), min=(m+om), sec=s})) end local g2uBoundary1 = p.parseISO8601('1582-10-15T00:00:00Z') local g2uBoundary2 = p.parseISO8601('1700-03-12T00:00:00Z') local g2uBoundary3 = p.parseISO8601('1800-03-13T00:00:00Z') local g2uBoundary4 = p.parseISO8601('1900-03-14T00:00:00Z') local g2uBoundary5 = p.parseISO8601('1918-01-26T00:00:00Z') -- декрет Ленина -- Передаваемое время обязано быть по Григорианскому календарю (новому стилю) function p.formatWiki( time, infocardClass, categoryNamePrefix ) if 'table' type( time ) then if time.args and time.args1 then time = tonumber( time.args1 ) else return 'unknown argument type: ' .. type( time ) .. ': ' .. table.tostring( time ) end end local t = os.date("*t", time) if time < g2uBoundary1 then -- выводим просто юлианский календарь. Задавать тут григорианский некорректно return p.formatWikiImpl( t, t, infocardClass, categoryNamePrefix ) end -- Специальные даты if t.year 1700 and t.month 3 and t.day 11 then return p.formatWikiImpl( {year=1700, month=2, day=29}, t, infocardClass, categoryNamePrefix) end if t.year 1800 and t.month 3 and t.day 12 then return p.formatWikiImpl( {year=1800, month=2, day=29}, t, infocardClass, categoryNamePrefix ) end if t.year 1900 and t.month 3 and t.day 13 then return p.formatWikiImpl( {year=1900, month=2, day=29}, t, infocardClass, categoryNamePrefix ) end if g2uBoundary1 <= time and time < g2uBoundary2 then return p.formatWikiImpl( os.date("*t", time - 10 * 24 * 60 * 60), t, infocardClass, categoryNamePrefix ) end if g2uBoundary2 <= time and time < g2uBoundary3 then return p.formatWikiImpl( os.date("*t", time - 11 * 24 * 60 * 60), t, infocardClass, categoryNamePrefix ) end if g2uBoundary3 <= time and time < g2uBoundary4 then return p.formatWikiImpl( os.date("*t", time - 12 * 24 * 60 * 60), t, infocardClass, categoryNamePrefix ) end if g2uBoundary4 <= time and time < g2uBoundary5 then return p.formatWikiImpl( os.date("*t", time - 13 * 24 * 60 * 60), t, infocardClass, categoryNamePrefix ) end --только Григорианский календарь return p.formatWikiImpl( t, t, infocardClass, categoryNamePrefix ) end function ternary ( cond , T , F ) if cond then return T else return F end end local nominativeMonthes = {'январь', 'февраль', 'март', 'апрель', 'май', 'июнь', 'июль', 'август', 'сентябрь', 'октябрь', 'ноябрь', 'декабрь'} local genitivusMonthes = {'января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря'} function nominativeYear( year ) if ( year >= 0 ) then return '' .. year .. '' else return '' .. ( 0 - year ) .. ' до н. э.' end end function inYear( year ) if ( year >= 0 ) then return '' .. year .. ' году' else return '' .. ( 0 - year) .. ' году до н. э.' end end function p.formatWikiImpl( t1, t2, infocardClass, categoryNamePrefix ) local nd = t2.day; local nm = t2.month; local ny = t2.year; local od = ternary ( t1.day ~= t2.day , t1.day, nil ); local om = ternary ( t1.month ~= t2.month , t1.month, nil ); local oy = ternary ( t1.year ~= t2.year , t1.year, nil ); local template = (nd ~= nil and "1" or "") .. (nm ~= nil and "2" or "") .. (ny ~= nil and "3" or "") .. (od ~= nil and "4" or "") .. (om ~= nil and "5" or "") .. (oy ~= nil and "6" or "") local datePart = '' if (template "12") then datePart = datePart .. string.format( "%d %s", nd, genitivusMonthesnm ) elseif (template "23") then datePart = datePart .. string.format( "%s %s", nominativeMonthesnm, nominativeYear( ny ) ) elseif (template "3") then datePart = datePart .. nominativeYear( ny ) elseif (template "123") then datePart = datePart .. string.format( "%d %s %s", nd, genitivusMonthesnm, nominativeYear( ny ) ) elseif (template "124") then datePart = datePart .. string.format( "%d (%d) %s", nd, genitivusMonthesnm, od, nd, genitivusMonthesnm ) elseif (template "1234") then datePart = datePart .. string.format( "%d (%d) %s %s", nd, genitivusMonthesnm, od, nd, genitivusMonthesnm, nominativeYear( ny ) ) elseif (template "1245") then datePart = datePart .. string.format( "%d %s (%d %s)", od, genitivusMonthesom, nd, genitivusMonthesnm ) elseif (template "12345") then datePart = datePart .. string.format( "%d %s (%d %s) %s", od, genitivusMonthesom, nd, genitivusMonthesnm, nominativeYear( ny ) ) elseif (template "123456") then datePart = datePart .. string.format( '%d %s %d (%d %s %s)', od, genitivusMonthesom, oy, nd, genitivusMonthesnm, nominativeYear( ny ) ) else datePart = datePart .. 'формат неверен' end datePart = datePart .. '' local infocardTemplate = (nd ~= nil and "1" or "") .. (nm ~= nil and "2" or "") .. (ny ~= nil and "3" or "") if infocardClass then if (infocardTemplate "123") then datePart = datePart .. '( - - )' elseif (infocardTemplate "23") then datePart = datePart .. '( - )' elseif (infocardTemplate "3") then datePart = datePart .. '( )' end end if categoryNamePrefix then if ( nd ~= nil and nm ~= nil) then datePart = datePart .. '[.. categoryNamePrefix .. ' ' .. nd .. ' ' .. genitivusMonthes[nm .. ']]' end if ( ny ~= nil) then datePart = datePart .. 'К:' .. categoryNamePrefix .. ' в ' .. inYear( ny ) .. '' end end return datePart end return p